using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using NUnit.Framework;
using ShadeTree.Binding.Drivers;
using ShadeTree.Binding.Lists;
using ShadeTree.Binding.WPF;
using ShadeTree.WinForms.Tests.WPF;

namespace ShadeTree.Binding.Tests.WPF
{
    [TestFixture]
    public class BindingElementsTester
    {
        #region Setup/Teardown

        [SetUp]
        public void SetUp()
        {
            childAgeBox = new TextBox();

            nameBox = new TextBox();
            nameLabel = new Label {Content = "Name"};

            activeBox = new CheckBox();

            ageBox = new TextBox();

            stateComboBox = new ComboBox();
            countyCombobox = new ComboBox();


            var countyList = new Picklist<string>("Barton", "Jasper", "Travis");
            ListRepository.SetList("county", countyList);

            ageLabel = new Label();

            binder = new ScreenBinder<ScreenBinderTarget>();
            binder.Bind(x => x.Name).To(nameBox).WithLabel(nameLabel).RebindOnChange();
            binder.Bind(x => x.Age).To(ageBox).WithAlias("TheAgeBox").OnLostFocus(() => onLostFocusWasCalled = true);
            binder.Bind(x => x.State).To(stateComboBox).WithAlias("State").FillWithValues("MO", "AR", "CT", "TX");
            binder.Bind(x => x.County).To(countyCombobox).FillWithList("county").AsReadOnly();
            binder.Bind(x => x.Age).To(ageLabel);
            binder.Bind(x => x.Active).To(activeBox);
            binder.Bind(x => x.Child.ChildAge).To(childAgeBox);
        }

        #endregion

        private TextBox nameBox;
        private Label nameLabel;
        private TextBox ageBox;
        private ComboBox stateComboBox;
        private ScreenBinder<ScreenBinderTarget> binder;
        private ComboBox countyCombobox;
        private bool onLostFocusWasCalled;
        private Label ageLabel;
        private CheckBox activeBox;
        private TextBox childAgeBox;


        [Test]
        public void BindATextBoxToANestedProperty()
        {
            var target = new ScreenBinderTarget {Age = 34, Name = "Jeremy", Child = new ChildTarget {ChildAge = 4}};
            binder.BindToModel(target);

            childAgeBox.Text.ShouldEqual("4");

            childAgeBox.Text = "23";
            ControlDriverFactory.GetDriver(childAgeBox).FireLostFocus();

            target.Child.ChildAge.ShouldEqual(23);
        }

        [Test]
        public void BindTheTextBoxesAndCheckValues()
        {
            var target = new ScreenBinderTarget {Age = 34, Name = "Jeremy"};
            binder.BindToModel(target);

            ageBox.Text.ShouldEqual(target.Age.ToString());
            nameBox.Text.ShouldEqual(target.Name);
        }


        [Test]
        public void BoundCheckboxSetsValue()
        {
            var target = new ScreenBinderTarget {Active = true};
            binder.BindToModel(target);

            activeBox.IsChecked.GetValueOrDefault(false).ShouldBeTrue();

            activeBox.IsChecked = false;

            target.Active.ShouldBeFalse();
        }

        [Test]
        public void BoundLabelGetsTheContent()
        {
            var target = new ScreenBinderTarget {Age = 4};
            binder.BindToModel(target);

            ageLabel.Content.ToString().ShouldEqual("4");
            ageLabel.HorizontalAlignment = HorizontalAlignment.Right;
        }

        [Test]
        public void Changing_an_element_also_calls_the_parent_screenbinder()
        {
            var target = new ScreenBinderTarget {Age = 34, Name = "Jeremy"};
            target.Age = 34;
            binder.BindToModel(target);


            new WpfTextBoxHandler().Enter(nameBox, "Jeremy + 1");
            target.Age.ShouldEqual(35);
            ageBox.Text.ShouldEqual("35");
        }

        [Test]
        public void FillWithListFromTheBinder()
        {
            var states = new[]
                             {
                                 new FakeState("MO"),
                                 new FakeState("AR"),
                                 new FakeState("TX")
                             };

            binder.FillList<FakeState>(stateComboBox, states, x => x.State);
            stateComboBox.Items.Count.ShouldEqual(3);
        }

        [Test]
        public void Find_element_by_field()
        {
            binder.FindElementByField("Name").Control.ShouldEqual(nameBox);
            binder.FindElementByField("Age").Control.ShouldEqual(ageBox);
        }

        [Test]
        public void Find_element_by_label_or_text()
        {
            binder.FindElement("Name").Control.ShouldEqual(nameBox);
            binder.FindElement("TheAgeBox").Control.ShouldEqual(ageBox);
        }

        [Test]
        public void Focusing_a_control_through_ScreenBinder_latches_and_the_lostfocus_events_cannot_fire()
        {
            var form = new Window();
            var panel = new StackPanel();
            form.Content = panel;

            foreach (Control control in new Control[] {nameLabel, ageBox, stateComboBox, countyCombobox})
            {
                panel.Children.Add(control);
            }

            form.Show();

            binder.Focus(ageBox);
            binder.Focus(stateComboBox);

            onLostFocusWasCalled.ShouldBeFalse();
        }

        [Test]
        public void GetThePropertyNameFromAnExpression()
        {
            PropertyInfo property = ReflectionHelper.GetProperty<ScreenBinderTarget>(x => x.Age);
            property.Name.ShouldEqual("Age");
        }

        [Test]
        public void MarkElementAsReadOnlyWithTheAsReadOnlyMethod()
        {
            binder.FindElementForControl(countyCombobox).ActivationMode.ShouldEqual(ActivationMode.ReadOnly);
        }

        [Test]
        public void The_picklist_expression_can_pull_lists_out_of_the_ListRepository()
        {
            countyCombobox.Items.Count.ShouldEqual(3);
            countyCombobox.Items[0].ShouldEqual("Barton");
            countyCombobox.Items[1].ShouldEqual("Jasper");
            countyCombobox.Items[2].ShouldEqual("Travis");
        }

        [Test]
        public void TheAliasIsAssigned()
        {
            binder.FindElementForControl(ageBox).Alias.ShouldEqual("TheAgeBox");
        }

        [Test]
        public void TheComboBoxIsBound()
        {
            var target = new ScreenBinderTarget {State = "TX"};
            binder.BindToModel(target);

            stateComboBox.SelectedItem.ShouldEqual("TX");

            target.State = "MO";
            binder.UpdateScreen();

            stateComboBox.SelectedItem.ShouldEqual("MO");
        }

        [Test]
        public void TheLabelIsAssigned()
        {
            binder.FindElementForControl(nameBox).Label.Control.ShouldEqual(nameLabel);
        }

        [Test]
        public void TheListWasFilledInOnTheComboBox()
        {
            stateComboBox.Items.Count.ShouldEqual(4);
            stateComboBox.Items.ShouldContain("MO");
            stateComboBox.Items.ShouldContain("AR");
            stateComboBox.Items.ShouldContain("CT");
            stateComboBox.Items.ShouldContain("TX");
        }
    }

    public enum OnOff
    {
        On,
        Off
    }

    public class ScreenBinderTarget
    {
        private double _age;
        private ChildTarget _child = new ChildTarget();
        private string _name;


        public bool Active { get; set; }

        public string County { get; set; }

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                _age++;
            }
        }

        public double Age
        {
            get { return _age; }
            set { _age = value; }
        }

        public string State { get; set; }

        public OnOff Switch { get; set; }

        public ChildTarget Child
        {
            get { return _child; }
            set { _child = value; }
        }
    }

    public class ChildTarget
    {
        public double ChildAge { get; set; }
    }

    public class FakeState : IComparable
    {
        private readonly string _state;


        public FakeState(string state)
        {
            _state = state;
        }


        public string State
        {
            get { return _state; }
        }

        #region IComparable Members

        public int CompareTo(object obj)
        {
            return 0;
        }

        #endregion
    }
}